package com.webbdong.aop.agent.test.asm;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * 定义一个类的生成 ClassWriter
 *
 * 设定版本、修饰符、全类名、签名、父类、实现的接口，其实也就是那句；public class HelloWorld
 *
 * 接下来开始创建方法，方法同样需要设定；修饰符、方法名、描述符等。这里面有几个固定标识；
 *
 * 类型描述符
 *
 * | Java 类型 | 类型描述符 |
 * |:---|:---|
 * | boolean | Z |
 * | char | C |
 * | byte | B |
 * | short | S |
 * | int | I |
 * | float | F |
 * | long | J |
 * | double | D |
 * | Object | Ljava/lang/Object; |
 * | int[] | [I |
 * | Object[][] | [[Ljava/lang/Object; |
 *
 * 方法描述符
 *
 * | 源文件中的方法声明 | 方法描述符 |
 * |:---|:---|
 * | void m(int i, float f) | (IF)V |
 * | int m(Object o) | (Ljava/lang/Object;)I |
 * | int[] m(int i, String s) | (ILjava/lang/String;)[I |
 * | Object m(int[] i) | ([I)Ljava/lang/Object; |
 *
 * ([Ljava/lang/String;)V == void main(String[] args)
 *
 * 执行指令；获取静态属性。主要是获得 System.out
 *
 * 加载常量 load constant，输出我们的HelloWorld methodVisitor.visitLdcInsn("Hello World");
 *
 * 最后是调用输出方法并设置空返回，同时在结尾要设置操作数栈的深度和局部变量的大小
 */
public class ASMDemo {


    private static byte[] generate() {
        ClassWriter classWriter = new ClassWriter(0);

        // 定义对象头；版本号、修饰符、全类名、签名、父类、实现的接口
        classWriter.visit(Opcodes.V1_8,
                Opcodes.ACC_PUBLIC,
                "org/itstack/demo/asm/AsmHelloWorld",
                null,
                "java/lang/Object",
                null);

        // 添加方法；修饰符、方法名、描述符、签名、异常
        MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC,
                "main",
                "([Ljava/lang/String;)V",
                null,
                null);
        // 执行指令；获取静态属性
        methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        // 加载常量 load constant
        methodVisitor.visitLdcInsn("Hello World");
        // 调用方法
        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        // 返回
        methodVisitor.visitInsn(Opcodes.RETURN);
        // 设置操作数栈的深度和局部变量的大小
        methodVisitor.visitMaxs(2, 1);
        // 方法结束
        methodVisitor.visitEnd();
        // 类完成
        classWriter.visitEnd();
        // 生成字节数组
        return classWriter.toByteArray();
    }
}
